#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>

#include <libusb.h>

#include "config.h"

#include "mp707r.h"
#include "mpcommon.h"

enum libusb_error exchange_feature_reports_port_state(libusb_device_handle *devh, unsigned int relay, bool state) {
    int transferred = 0;
    unsigned char in[8];
    unsigned char out[8];
    int i = 0;
    enum libusb_error result = LIBUSB_SUCCESS;

    for (i=0; i < 8; i++) {
	out[i]=0x00;
	in[i]=0x00;
    }

    out[0] = 0xe7;
    out[1] = state ? 0x18 : 0x00;
    out[2] = 0x00;
    out[3] = 0x00;
    out[4] = 0x00;
    out[5] = 0x00;
    out[6] = 0x00;
    out[7] = 0x00;

    if (result = send_feature_report(devh, out, sizeof(out), &transferred) == LIBUSB_SUCCESS && transferred > 0) {
	if (result = receive_feature_report(devh, in, sizeof(in), &transferred) == LIBUSB_SUCCESS && transferred > 0) {

	} else {
	    fprintf(stderr, "Error receiving feature report %d\n", result);
	}
    } else {
	fprintf(stderr, "Error sending feature report %d\n", result);
    }

    return result;
}

void switch_relay(int id, int relay, bool state) {
    struct		libusb_device_handle		*devh = NULL;
    libusb_device					*dev = NULL;
    libusb_context 					*ctx = NULL;
    enum		libusb_error			result;

    result = libusb_init(&ctx);
    libusb_set_debug(ctx, 0x03);

    if (result == LIBUSB_SUCCESS) {
	result = lookup_device(ctx, &devh, id, PRODUCT_FAMILY_CODE);
	if (result == LIBUSB_SUCCESS) {
	    if (devh != NULL) {
		result = exchange_feature_reports_port_state(devh, relay, state);
		if (result == LIBUSB_SUCCESS || result > 0) {

		} else {
		    fprintf(stderr, "Error communicating with the device, device will be reseted\n");
		    libusb_reset_device(devh);
		}
		libusb_close(devh);
	    } else {
		fprintf(stderr, "Unable to find the device\n");
	    }
	} else {
	    fprintf(stderr, "Unable to find the device\n");
	}
	libusb_exit(ctx);
    } else {
	fprintf(stderr, "libusb_init failed\n");
    }
}

void print_devices_list() {
    struct		libusb_device_handle		*devh = NULL;
    libusb_device					**devices = NULL;
    libusb_context 					*ctx = NULL;
    enum		libusb_error			result;

    result = libusb_init(&ctx);
    libusb_set_debug(ctx, 0x03);

    if (result == LIBUSB_SUCCESS) {
	int count = libusb_get_device_list(ctx, &devices);
	
	if (count > 0) {
	    for (count--; count >= 0; count--) {
		struct libusb_device_descriptor *desc = calloc(1, sizeof(struct libusb_device_descriptor));
		unsigned int id = 0;
		unsigned char family = 0;
		result = libusb_get_device_descriptor(devices[count], desc);
		if (result ==  LIBUSB_SUCCESS) {
		    if (desc->idVendor == VENDOR_ID && desc->idProduct == PRODUCT_ID) {
			result = libusb_open(devices[count], &devh);
			if (result == LIBUSB_SUCCESS) {
			    result = exchange_feature_reports_dev_id(devh, &id, &family);
			    if (result == LIBUSB_SUCCESS || result > 0) {
				fprintf(stdout, "Bus:0x%02X Device:0x%02X VID:0x%04X PID:0x%04X MP-FAMILY:0x%02X MP-ID:0x%08X\n", libusb_get_bus_number(devices[count]), libusb_get_device_address(devices[count]), desc->idVendor, desc->idProduct, family, id);
			    } else {
				fprintf(stderr, "Error communicating with the device, device will be reseted\n");
				libusb_reset_device(devh);
			    }
			    libusb_close(devh);
			} else {
			    fprintf(stderr, "Unable to open device\n");
			}
		    }
		} else {
		    fprintf(stderr, "Unable to get device descriptor\n");
		}
		free(desc);
	    }
	}
	libusb_free_device_list(devices, true);

	libusb_exit(ctx);
    } else {
	fprintf(stderr, "libusb_init failed\n");
    }
}

int main (int argc, char *argv[]) {
    unsigned int					id = 0;
    unsigned int					relayno;
    unsigned char					*relaystate = calloc(1, sizeof(unsigned char)*6);

    if ( argc == 2 && (strncmp(argv[1], "help", 4) == 0 || strncmp(argv[1], "--help", 6) == 0 || strncmp(argv[1], "-h", 2) == 0 ) ) {
	fprintf(stdout, "Simple program for the MP707R USB HID Device, which can control two relays\n");
	fprintf(stdout, "More details available on the manufacturer website at http://www.masterkit.ru\n");
	fprintf(stdout, "Assuming device with VID=0x%X and PID=0x%X\n\n", VENDOR_ID, PRODUCT_ID);
	fprintf(stdout, "  Main usage: mp707r [id:devid] [port:state] ... [port:state]\n");
	fprintf(stdout, "       Where: port:state is string like \"port1:on\" with port number and desired state of the relay\n");
	fprintf(stdout, "              id:devid is string like \"id:270\" or \"id:0x1e0\" with device id (additionaly, running mp707r -l list all devices on the bus with ids)\n\n");
	fprintf(stdout, "   Examples: mp707r port0:on port2:off\n");
	fprintf(stdout, "             mp707r id:124 port0:true port2:false\n\n");

    } else if ( argc == 2 && (strncmp(argv[1], "list", 4) == 0 || strncmp(argv[1], "--list", 6) == 0 || strncmp(argv[1], "-l", 2) == 0 ) ) {
	print_devices_list();

    } else if ( argc >= 2 ) {
	unsigned int counter = 1;
	for ( ; counter < argc; counter++) {
	    if (sscanf(argv[counter], "port%u:%5s", &relayno, relaystate) == 2 || sscanf(argv[counter], "port%x:%5s", &relayno, relaystate) == 2) {
		if (strncmp(relaystate, "on", 5) == 0) {
		    switch_relay(id, relayno, true);
		} else if (strncmp(relaystate, "true", 5) == 0) {
		    switch_relay(id, relayno, true);
		} else if (strncmp(relaystate, "off", 5) == 0) {
		    switch_relay(id, relayno, false);
		} else if (strncmp(relaystate, "false", 5) == 0) {
		    switch_relay(id, relayno, false);
		}
	    } else if (sscanf(argv[counter], "id:0x%X", &id) == 1 || sscanf(argv[counter], "id:%u", &id) == 1) {

	    }
	}

    } else {
	fprintf(stderr, "Missing or unknown parameters, please use --help or -h options for more information\n");
    }

    return 0;
}
